﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Xml;
using System.Data;
using System.Data.OleDb;
using System.Threading;
using System.Net;

namespace sunsetmp
{
    public class ThrearWorkClass
    {
        Style ItemStyle;
        Core core;
        Properties.Settings ps;
        Dictionary<int, string> genresDec;
        public Dictionary<string, DataTable> tableDec;
        int webTableRowCount = 0;
        BackgroundWorker webWorker;

        private struct LocalElem
        {
            public DataTable table;
            public string[] pathMass;
        }

        public ThrearWorkClass(Style itemStyle, Core c)
        {
            core = c;
            ItemStyle = itemStyle;
            ps = new Properties.Settings();
            genresDec = new Dictionary<int, string>();
            tableDec = new Dictionary<string, DataTable>();
            #region Genres
            genresDec.Add(1,"Rock");
            genresDec.Add(2,"Pop");
            genresDec.Add(3,"Rap & Hip-Hop");
            genresDec.Add(4, "Easy Listening");
            genresDec.Add(5,"House & Dance");
            genresDec.Add(6,"Instrumental");
            genresDec.Add(7,"Metal");
            genresDec.Add(8,"Dubstep");
            genresDec.Add(9,"Jazz & Blues");
            genresDec.Add(10,"Drum & Bass");
            genresDec.Add(11,"Trance");
            genresDec.Add(12, "Chanson");
            genresDec.Add(13,"Ethnic");
            genresDec.Add(14,"Acoustic & Vocal");
            genresDec.Add(15,"Reggae");
            genresDec.Add(16,"Classical");
            genresDec.Add(17,"Indie Pop");
            genresDec.Add(18, "Other");
            genresDec.Add(19, "Speech");
            genresDec.Add(21,"Alternative");
            genresDec.Add(22,"Electropop & Disco");
            #endregion

        }

        /// <param name="code">
        /// 0 - add local track; 
        /// 1 - add web track;
        /// 2 - update web track;
        /// 3 - delete web track; 
        /// 4 - captcha create;
        /// </param>
        /// /// <param name="data">
        /// 0 - DataRow; 
        /// 1 - AddedElem; 
        /// 2 - UpdateElem; 
        /// 3 - null; 
        /// 4 - param array: [0] - code; [1] - id; [2] - offset; [3] - count; [4] - captchaUrl; [5] - captchaSid;
        /// </param>
        private void ReportProgress(BackgroundWorker worker,int code, object data)
        {
            Thread.Sleep(3);
             worker.ReportProgress(code, data);
        }

        private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            DataRow row;
            switch (e.ProgressPercentage)
            {
                case 0:
                    row = (DataRow)e.UserState;
                    if (tableDec.ContainsKey(row.Table.TableName) == false)
                    {
                        BackgroundWorker w = (BackgroundWorker)sender;
                        w.CancelAsync();
                        return;
                    }
                    row.Table.Rows.Add(row);
                    return;
                case 1:
                    row = (DataRow)e.UserState;
                    row.Table.Rows.Add(row);
                    return;
                case 4:
                    DialogWindow.CaptchaWindow cw = new DialogWindow.CaptchaWindow(this,(object[]) e.UserState);
                    break;
            }
        }

        private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Result != null)
                core.UpdateTable((DataTable)e.Result);
        }

        //------------------------------------------
         #region Локальные файлы

        public void AddLocalTrack(DataTable table, string[] pathMass)
        {
            LocalElem data = new LocalElem();
            data.pathMass = pathMass;
            data.table = table;
            BackgroundWorker locWorker = new BackgroundWorker();
            locWorker.WorkerReportsProgress = true;
            locWorker.DoWork+=locWorker_DoWork;
            locWorker.ProgressChanged += worker_ProgressChanged;
            locWorker.RunWorkerCompleted+=worker_RunWorkerCompleted;
            locWorker.WorkerSupportsCancellation = true;
            tableDec.Add(table.TableName, table);
            locWorker.RunWorkerAsync(data);
        }

        private void locWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;
            LocalElem data = (LocalElem)e.Argument;
            int i = data.table.Rows.Count;
            if (data.table != null)
            {
                string[] files = GetSubDirFile(data.pathMass);
                int count = int.Parse(files[files.Length - 1]);
                for (i = 0; i < count; i++)
                    if (MusicFormatChecker(files[i]))
                        InsertLocalTrack(worker, files[i], i, data.table);

                for (int j = count; j < files.Length - 1; j++)
                    foreach (string path in Directory.GetFiles(files[j]))
                        if (MusicFormatChecker(path))
                        {
                            i++;
                            InsertLocalTrack(worker, path, i, data.table);
                        }
                core.UpdateTable(data.table);
                tableDec.Remove(data.table.TableName);
                e.Result = data.table;
            }
        }

        private void InsertLocalTrack(BackgroundWorker worker,string path, int index, DataTable table)
        {
            DataRow row = table.NewRow();
            row[2] = path;
            row[0] = index;
            string ext = Path.GetExtension(path).ToLower();
            if (ext == ".pls")
            {
                StreamReader sr = new StreamReader(path);
                sr.ReadLine();
                int count = Convert.ToInt32(sr.ReadLine().Split('=')[1]);
                if (count!= 0)
                    row[2] = sr.ReadLine().Replace("File1=", "");
                sr.Close();
            }
            String artist = null;
            String artTitle = null;
            String genres = null;
            String album = null;
            Int32 year = 0;
            try
            {
                TagLib.File f = TagLib.File.Create(path);
                artist = f.Tag.Performers[0];
                artTitle = artist + " - " + f.Tag.Title;
                if (f.Tag.FirstGenre != null)
                    genres = f.Tag.Genres[0];
                if (f.Tag.Album != null)
                    album = f.Tag.Album;
                if (f.Tag.Year != 0)
                    year = Convert.ToInt32(f.Tag.Year);
                row[3] = artTitle;
                row[4] = artist;
                row[5] = album;
                row[1] = year;
                row[6] = genres;
                ReportProgress(worker, 0, row);
            }
            catch
            {
                row[3] = Path.GetFileNameWithoutExtension(path);
                ReportProgress(worker, 0, row);
            }
        }

        private string[] GetSubDirFile(string[] pathArray)
        {
            List<string> files = new List<string>();
            List<string> directories = new List<string>();
            foreach (string p in pathArray)
            {
                if (MusicFormatChecker(p))
                    files.Add(p);
                else if (Directory.Exists(p))
                    directories.Add(p);
            }
            for (int i = 0; i < directories.Count; i++)
            {
                string path = directories[i];
                foreach (var p in Directory.GetDirectories(path))
                    directories.Add(p);
            }
            int count = files.Count;
            files.AddRange(directories);
            files.Add(count.ToString());
            return files.ToArray();
        }

        private bool MusicFormatChecker(string path)
        {
            string res = Path.GetExtension(path).ToLower();
            string[] formats = {".mp3",".wav",".flac",".midi",".ogg",".wma", ".m4a", ".m3u", ".pls" };
            foreach (string f in formats)
                if (res == f)
                    return true;
            return false;
        }

        #endregion

        //-------------------------------------------
        #region Музыка Вконтакте

        public void AddWebTrack(DataTable table)
        {
            webWorker = new BackgroundWorker();
            webWorker.WorkerReportsProgress = true;
            webWorker.DoWork += webWorker_DoWork;
            webWorker.ProgressChanged += worker_ProgressChanged;
            webWorker.RunWorkerCompleted += worker_RunWorkerCompleted;
            webWorker.WorkerSupportsCancellation = true;
            if (table.Rows.Count != 0)
                webTableRowCount = Convert.ToInt32(table.Rows[0].ItemArray[0]);
            webWorker.RunWorkerAsync(table);
        }

        public void CancelWebAddition()
        {
            if (webWorker != null)
                webWorker.CancelAsync();
        }

        private void webWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;
            DataTable table = (DataTable)e.Argument;
            XmlDocument doc = new XmlDocument();
            int[] pidArray = new int[200];
            int code = Convert.ToInt16(table.TableName.Split('#')[0]);
            string id = table.TableName.Split('#')[1];
            string albumId = null;
            if (table.TableName.Split('#')[2].ToString() != "")
                albumId = table.TableName.Split('#')[2].ToString();
            int maxCount = GetMaxCount(id, code, albumId);
            string xmlDoc = "";
            int offset = 0;
            int count;
            if (code % 2 == 0)
                count = 500;
            else
                count = 20;
            while (offset < maxCount)
            {
                xmlDoc = GetXmlString(code, id, offset, count);
                offset += count;
                if (xmlDoc.IndexOf("error_code") != -1 || xmlDoc.IndexOf("audio") == -1)
                {
                    if (xmlDoc.IndexOf("<error_msg>Captcha needed</error_msg>") != -1)
                    {
                        xmlDoc = CaptchaError(xmlDoc, new object[] { code, id, offset, count },worker);
                        if (xmlDoc.IndexOf("error_code") != -1)
                        {
                            CancelWebAddition();
                            return;
                        }
                    }
                    else
                        continue;
                }
                doc.LoadXml(xmlDoc);
                
                XmlNodeList nodeArray;
                if (code % 2 == 1)
                {
                    try
                    {
                        var buf = doc.LastChild.FirstChild.ChildNodes[0].FirstChild.Value.Split(',');
                        for (int i = 0; i < buf.Length - 1; i++)
                            pidArray[i] = Convert.ToInt32(buf[i]);
                    }
                    catch 
                    {
                        continue;
                    }
                    xmlDoc = GetXmlString(11, doc.LastChild.FirstChild.ChildNodes[1].FirstChild.Value, 0, 0);
                    doc.RemoveAll();
                    doc.LoadXml(xmlDoc);
                }
                if (xmlDoc.IndexOf("error_code") != -1 || xmlDoc.IndexOf("audio") == -1)
                    continue;
                if (code == 6)
                    doc.LastChild.RemoveChild(doc.LastChild.ChildNodes[0]);
                nodeArray = doc.LastChild.ChildNodes;
                for (int i = 0; i < nodeArray.Count; i++)
                {
                    if (nodeArray.Item(i) != null)
                    {
                        DataRow row;
                        if (code % 2 == 0)
                                row = CreateWebAudioRow(nodeArray.Item(i), table);
                        else
                            row = CreateWebWallRow(nodeArray.Item(i), table, pidArray[i]);
                        if (worker.CancellationPending)
                            return;
                        ReportProgress(worker, 1, row);
                    }
                }
            }
            e.Result = table;
        }

        string newXml;
        private string CaptchaError(string xmlWithCapcha, object[] requestInfo,BackgroundWorker worker)
        {
            XmlDocument doc = new XmlDocument();
             newXml = "error_code";
            doc.LoadXml(xmlWithCapcha);
            string url = doc.ChildNodes[1].ChildNodes[4].LastChild.Value.ToString();
            string cId = doc.ChildNodes[1].ChildNodes[3].LastChild.Value.ToString();
            object [] info = new object[6];
            for (int i=0;i<4;i++)
                info[i] = requestInfo[i];
            info[4] = url;
            info[5] = cId;
            ReportProgress(worker, 4, info);
            for (int t = 0; t < 120; t++)
            {
                if (newXml == "error_code")
                    Thread.Sleep(1000);
                else
                    break;
            }
            return newXml;
        }

        public void SendCaptcha(string captchaText, string captchaId, object[] request)
        {
            int code =(int) request[0];
            string id = request[1].ToString();
            int offset = (int) request[2];
            int count = (int) request[3];
            newXml = GetXmlString(code, id, offset, count, captchaId, captchaText);
        }

        private int CalculatePostCount(DataTable table)
        {
            int count = 0;
            int buf = -1;
            for (int i = 0; i < table.Rows.Count; i++)
            {
                int value = int.Parse(table.Rows[i].ItemArray[8].ToString());
                if (value != buf)
                {
                    count++;
                    buf = value;
                }
            }
            return count;
        }

        /// <param name="code">0 - UserAudio; 1 - UserWallAudioIds; 2 - GroupAudio; 3 - GroupWall; 4 - FriendAudio; 
        /// 5 - FriendWall; 6 - SearchAudio; 9 - Newsfeed; 11 - AudioById; 10 - UserAlbum; 20 - GroupAlbum; 40 - FriendAlbum;
        /// 31 - UserGroupList; 51 - UserFriendList; 71 - UserAlbumList</param>
        public string GetXmlString(int code, string id, int offset, int count)
        {
            string[] arr = new string[]{id,count.ToString(),offset.ToString()};
            switch (code)
            {
                case 0:
                    return core.GetRequest(string.Format("audio.get.xml?uid={0}&count={1}&offset={2}",arr));
                case 1:
                    return core.GetRequest(string.Format("execute.GetWallAudio.xml?owner_id={0}&count={1}&offset={2}", arr));
                case 2:
                    return core.GetRequest(string.Format("audio.get.xml?gid={0}&count={1}&offset={2}", arr));
                case 3:
                    return core.GetRequest(string.Format("execute.GetWallAudio.xml?owner_id=-{0}&count={1}&offset={2}", arr));
                case 4:
                    goto case 0;
                case 5:
                    goto case 1;
                case 6:
                    return core.GetRequest(string.Format("audio.search.xml?q={0}&auto_complete=1&sort=2&count={1}&offset={2}",arr));
                case 11:
                    return core.GetRequest(string.Format("audio.getById.xml?audios={0}",id));
                case 31:
                    return core.GetRequest(string.Format("groups.get.xml?uid={0}&extended=1",id));
                case 51:
                    return core.GetRequest(string.Format("friends.get.xml?uid={0}&fields=uid,first_name, last_name&name_case=nom&order=hints",id));
                case 71:
                    return core.GetRequest(string.Format("audio.getAlbums.xml?owner_id={0}",id));
                default:
                    return "error_code";
            }
            //if (code == 9) 
                //return core.GetRequest("execute.GetNewsAudio.xml?start_time=" + basicStartTime +"&end_time=" + basicEndTime +"&offset=" + count.ToString() +"&count=20");
        }

        public string GetXmlString(int code, string id, int offset, int count, string captchaId, string captchaText)
        {
            string[] arr = new string[] { id, count.ToString(), offset.ToString(),captchaId,captchaText };
            switch (code)
            {
                case 6:
                    return core.GetRequest(string.Format("audio.search.xml?q={0}&auto_complete=1&sort=2&count={1}&offset={2}&captcha_sid={3}&captcha_key={4}", arr));
                default:
                    return "error_code";
            }
        }

        public void GetUserGroups(string id, DataTable table)
        {
            DataTable countTable = core.FillGroupCountList();
            DataColumn[] keys = new DataColumn[1];
            keys[0] = countTable.Columns[0];
            countTable.PrimaryKey = keys;
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(GetXmlString(31,id,0,0));
            XmlNodeList nodeList = doc.LastChild.ChildNodes;
            int count = nodeList.Count - 1;
            DataRow row;
            DataRow findRow;
            for (int i = 0; i < count; i++)
            {
                int grId = Convert.ToInt32(nodeList[i + 1].ChildNodes[0].LastChild.Value);
                    row = table.NewRow();
                    row[0] = grId;
                    if ((findRow = countTable.Rows.Find(grId)) != null)
                        row[1] = findRow[1];
                    else
                    {
                        row[1] = 0;
                        core.InsertNewVkGroup(row);
                    }
                    row[2] = GarbageDel(nodeList[i + 1].ChildNodes[1].LastChild.Value);
                    table.Rows.Add(row);
            }
        }

        public void GetUserFriends(string id, DataTable table)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(GetXmlString(51, id, 0, 0));
            XmlNodeList nodeList = doc.LastChild.ChildNodes;
            DataRow row;
            foreach (XmlNode node in nodeList)
            {
                row = table.NewRow();
                row[0] = Convert.ToInt32(node.ChildNodes[0].LastChild.Value);
                row[1] = node.ChildNodes[2].LastChild.Value.ToString() + " " + node.ChildNodes[1].LastChild.Value.ToString();
                table.Rows.Add(row);
            }
        }

        public void GetUserAlbums(string id, DataTable table)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(GetXmlString(71, id, 0, 0));
            XmlNodeList nodeList = doc.LastChild.ChildNodes;
            DataRow row;
            foreach (XmlNode node in nodeList)
            {
                row = table.NewRow();
                row[0] = Convert.ToInt32(node.ChildNodes[1].LastChild.Value);
                row[1] = node.ChildNodes[2].LastChild.Value.ToString();
                table.Rows.Add(row);
            }
        }

        public Uri RefrashTrackUrl(DataRow row)
        {
            Uri url = new Uri(row.ItemArray[2].ToString());
            if (!CheckUrl(url.ToString()))
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(GetXmlString(11,row.ItemArray[1].ToString() + "_" + row.ItemArray[0].ToString(),0,0));
                if (doc.LastChild.Name == "error")
                {
                    if (doc.LastChild.FirstChild.FirstChild.Value == "5")
                        core.LogIn(true);
                }
                else
                    url = new Uri(doc.LastChild.FirstChild.ChildNodes[5].FirstChild.Value);
            }
            return url;
        }

        private bool CheckUrl(string url)
        {
            WebRequest request = WebRequest.Create(url);
            try
            {
                HttpWebResponse res = request.GetResponse() as HttpWebResponse;
                if (res.StatusDescription == "OK")
                    return true;
            }
            catch
            {
            }
            return false;
        }

        /// <param name="index">0 - UserAudio; 1 - UserWallAudioIds; 2 - GroupAudio; 3 - GroupWall; 4 - FriendAudio; 
        /// 5 - FriendWall; 6 - SearchAudio; 9 - Newsfeed; 11 - AudioById; 10 - UserAlbum; 20 - GroupAlbum; 40 - FriendAlbum;
        /// 31 - UserGroupList; 51 - UserFriendList</param>
        private int GetMaxCount(string id, int index, string albumId)
        {
            XmlDocument docCount = new XmlDocument();
            string countXml ="";
            int vkCount = 0;

            if (index == 0 || index == 4)
                countXml = core.GetRequest("audio.getCount.xml?oid=" + id);
            if (index == 2)
                countXml = core.GetRequest("audio.getCount.xml?oid=-" + id);
            if (index == 1 || index == 5)
                countXml = core.GetRequest("execute.GetWallCount.xml?id=" + id);
            if (index == 3)
                countXml = core.GetRequest("execute.GetWallCount.xml?id=-" + id);
            if (index == 6)
                countXml = core.GetRequest("execute.GetSearchCount.xml?q=" + id);
            if (index == 9)
                return 6000;
            if (countXml.IndexOf("Captcha needed") != -1)
                return 100;
            docCount.LoadXml(countXml);
            vkCount = Convert.ToInt32(docCount.LastChild.LastChild.Value);
            return vkCount;
        }

        public DataRow CreateWebAudioRow(XmlNode addedNoda, DataTable table)
        {
            int aid = int.Parse(addedNoda.ChildNodes[0].FirstChild.Value);
            int oid = int.Parse(addedNoda.ChildNodes[1].FirstChild.Value);
            string title = "";
            string artist ="";
            if (addedNoda.ChildNodes[2].FirstChild != null)
                artist = GarbageDel(addedNoda.ChildNodes[2].FirstChild.Value);
            if (artist.Length > 200)
                artist = artist.Remove(200);
            if (addedNoda.ChildNodes[3].FirstChild != null)
                title = GarbageDel(addedNoda.ChildNodes[3].FirstChild.Value);
            if (title.Length > 200)
                title = title.Remove(200);
            int duration = int.Parse(addedNoda.ChildNodes[4].FirstChild.Value);
            string url = addedNoda.ChildNodes[5].FirstChild.Value;
            int genre = -1;
            if (addedNoda.LastChild.Name == "genre")
                genre = int.Parse(addedNoda.LastChild.FirstChild.Value);
            DataRow row;
            try
            {  row = table.NewRow();  }
            catch (ArgumentOutOfRangeException)
            {  row = table.NewRow();  }

            row[0] = aid;
            row[1] = oid;
            row[2] = url;
            if (artist != null)
                row[4] = artist;
            if (title == null)
                title = "";
            row[6] = duration;
            try { row[5] = genresDec[genre]; }
            catch { }
            row[3] = row[4] + " - " + title;
            return row;
        }

        private DataRow CreateWebWallRow(XmlNode addedNoda, DataTable table, int pid)
        {
            DataRow row = CreateWebAudioRow(addedNoda, table);
            row[7] = pid;
            return row;
        }

       static private string GarbageDel(string data)
       {
           string[] dic = new string[] { "amp;", "♥", "█", "◄", "►", "★" };
           if (data == null)
               return null;
           foreach (var s in dic)
               data = data.Replace(s, string.Empty);
           return data;
       }

        #endregion
    }
}
